package com.zmn.uac.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zmn.common.dto2.ResponseDTO;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * 类描述：ios工具类
 *
 * @author xujie
 * @since 2021/06/16 16:19
 */
@Slf4j
public class AppleLoginUtils {

    private static final String TAG = "ios工具类";

    private static final String APPLE_AUTH_URL = "https://appleid.apple.com/auth/keys";

    private static Map<String, Map<String, String>> map;

    private static String kty;

    static {

        try {
            String str = HttpClientsUtil.executeGet(APPLE_AUTH_URL);
            JSONObject data = JSONObject.parseObject(str);
            JSONArray jsonArray = data.getJSONArray("keys");
            map = new HashMap<>();
            for (int i = 0; i < jsonArray.size(); i++) {
                kty = jsonArray.getJSONObject(i).getString("kty");
                Map<String, String> m = new HashMap<>();
                m.put("n", jsonArray.getJSONObject(i).getString("n"));
                m.put("e", jsonArray.getJSONObject(i).getString("e"));
                map.put(jsonArray.getJSONObject(i).getString("kid"), m);
            }
        } catch (Exception e) {
            log.error("[{}], ios授权url异常：{}", TAG, e.getMessage(), e);
        }
    }

    /**
     * 获取苹果的公钥
     *
     * @param kid 密钥类型
     * @return PublicKey
     * @author xujie
     * @since 2021/06/16
     */
    public static PublicKey getPublicKey(String kid) {

        try {
            String n = map.get(kid).get("n");
            String e = map.get(kid).get("e");
            BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
            BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));
            RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
            //目前kty均为 "RSA"
            KeyFactory kf = KeyFactory.getInstance(kty);
            return kf.generatePublic(spec);
        } catch (Exception e) {
            log.error("[{}], 获取苹果的公钥异常：{}", TAG, e.getMessage(), e);
        }
        return null;
    }

    /**
     * 对前端传来的JWT字符串identityToken的第二部分进行解码
     * 主要获取其中的aud和sub，aud对应ios前端的包名，sub对应当前用户的授权openID
     *
     * @param identityToken identityToken
     * @return Map
     * @author xujie
     * @since 2021/06/16
     */
    public static Map<String, JSONObject> parserIdentityToken(String identityToken) {

        Map<String, JSONObject> map = new HashMap<>(16);
        String[] arr = identityToken.split("\\.");
        String deHeader = new String(Base64.decodeBase64(arr[0]));
        JSONObject header = JSON.parseObject(deHeader);
        map.put("header", header);
        String dePayload = new String(Base64.decodeBase64(arr[1]));
        JSONObject payload = JSON.parseObject(dePayload);
        map.put("payload", payload);
        return map;
    }

    /**
     * 对前端传来的identityToken进行验证
     *
     * @param identityToken identityToken
     * @param appleAud      appleAud
     * @return openId
     * @author xujie
     * @since 2021/06/16
     */
    public static ResponseDTO<String> verifyToken(String identityToken, String appleAud) {

        String appleIssuerUrl = "https://appleid.apple.com";
        Map<String, JSONObject> json = parserIdentityToken(identityToken);
        try {
            // 1 解析
            JSONObject header = json.get("header");
            String kid = header.getString("kid");
            // 2 生成publicKey
            PublicKey publicKey = getPublicKey(kid);
            if (publicKey == null) {
                return ResponseDTO.fail("获取苹果验证公钥失败");
            }
            // 3 验证  https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
            JwtParser jwtPaser = Jwts.parserBuilder()
                    .requireIssuer(appleIssuerUrl)
                    .requireAudience(appleAud)
                    .setSigningKey(publicKey).build();
            Jws<Claims> claim = jwtPaser.parseClaimsJws(identityToken);
            if (claim != null) {
                //sub,即用户的Apple的openId
                String sub = claim.getBody().get("sub").toString();
                String iss = claim.getBody().get("iss").toString();
                String aud = claim.getBody().get("aud").toString();
                log.info("[{}], sub:{},iss:{},aud:{}", TAG, sub, iss, aud);
                long exp = Long.parseLong(claim.getBody().get("exp").toString()) * 1000;
                if (appleIssuerUrl.equals(iss) && appleAud.equals(aud) && System.currentTimeMillis() < exp) {
                    return ResponseDTO.success(sub);
                }
            }
            log.error("[{}], parserIdentityToken:{}, 苹果登陆授权 idToken 验证失败", TAG, json);
            return ResponseDTO.fail("苹果登陆授权 idToken 验证失败");
        } catch (ExpiredJwtException e) {
            log.error("[{}], parserIdentityToken:{}, 苹果登陆授权 idToken 已过期：{}", TAG, json, e.getMessage(), e);
            return ResponseDTO.fail("苹果登陆授权 idToken 已过期");
        } catch (Exception e) {
            log.error("[{}], parserIdentityToken:{}, 非法的苹果登陆授权 idToken：{}", TAG, json, e.getMessage(), e);
            return ResponseDTO.fail("非法的苹果登陆授权 idToken");
        }
    }

}
